use std::str::FromStr;

use clap::Parser;
use nom::{
    branch::alt, bytes::complete::{is_not, tag}, character::complete::char, combinator::map, multi::{many0, separated_list0}, IResult
};

trait ReplaceData {
    fn key_value(&self) -> (&Vec<String>, &Vec<String>);
}

struct Result {
    changed: bool,
}

impl Result {
    fn new() -> Self {
        Self {
            changed: false,
        }
    }
}

#[derive(clap::Subcommand, Debug)]
enum Command {
    #[command(about = "根据键值对替换")]
    KeyValue(KeyValue),
    #[command(about = "根据meta文件的规则替换")]
    MetaMatch(MetaMatch),
    #[command(about = "根据submeta文件的规则替换")]
    SubMetaMatch(SubMetaMatch),
}

#[derive(clap::Parser, Debug)]
struct SubMetaMatch {
    #[arg(long, help = "根据importer匹配")]
    importer: Option<String>,
    #[arg(long, help = "根据uuid匹配")]
    uuid: Option<String>,
    #[arg(long, help = "根据id匹配")]
    id: Option<String>,
    #[arg(long, help = "根据name匹配")]
    name: Option<String>,
    #[arg(short, long, help = "是否递归", default_value_t = false)]
    recursive: bool,
    #[arg(short, long, help = "修改的key，如果是深层次的key，可以使用/分隔，且会转义//表示/")]
    key: Vec<String>,
    #[arg(short, long, help = "修改的value，类型根据Json的规则解析，如果是字符串，需要用引号包裹，如果是数组，需要用[]包裹")]
    value: Vec<String>,
}

impl SubMetaMatch {
    fn try_match(&self, json:&mut serde_json::Value, result: &mut Result) {
        if self.is_match(json) {
            replace_json(json, self, result);
        }

        if self.recursive {
            if let Some(object) = json.as_object_mut() {
                if let Some(submeta) = object.get_mut("subMetas") {
                    if let Some(submeta) = submeta.as_object_mut() {
                        for submeta in submeta.values_mut() {
                            self.try_match(submeta, result);
                        }
                    }
                }
            }
        }
    }

    fn is_match(&self, json:&serde_json::Value)->bool {
        let Some(object)  = json.as_object() else {
            return false;
        };

        if let Some(ref importer) = self.importer {
            if let Some(value) = object.get("importer") {
                if let Some(value) = value.as_str() {
                    if value != importer {
                        return false;
                    }
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        if let Some(ref uuid) = self.uuid {
            if let Some(value) = object.get("uuid") {
                if let Some(value) = value.as_str() {
                    if value != uuid {
                        return false;
                    }
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        if let Some(ref id) = self.id {
            if let Some(value) = object.get("id") {
                if let Some(value) = value.as_str() {
                    if value != id {
                        return false;
                    }
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        if let Some(ref name) = self.name {
            if let Some(value) = object.get("name") {
                if let Some(value) = value.as_str() {
                    if value != name {
                        return false;
                    }
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        true
    }
}

impl ReplaceData for SubMetaMatch {
    fn key_value(&self) -> (&Vec<String>, &Vec<String>) {
        (&self.key, &self.value)
    }
}

#[derive(clap::Parser, Debug)]
struct MetaMatch {
    #[arg(long, help = "根据importer匹配")]
    importer: Option<String>,
    #[arg(long, help = "根据uuid匹配")]
    uuid: Option<String>,
    #[arg(short, long, help = "修改的key，如果是深层次的key，可以使用/分隔，且会转义//表示/")]
    key: Vec<String>,
    #[arg(short, long, help = "修改的value，类型根据Json的规则解析，如果是字符串，需要用引号包裹，如果是数组，需要用[]包裹")]
    value: Vec<String>,
}

impl MetaMatch {
    fn try_match(&self, json:&mut serde_json::Value, result: &mut Result) {
        let Some(object)  = json.as_object() else {
            return;
        };

        if let Some(ref importer) = self.importer {
            if let Some(value) = object.get("importer") {
                if let Some(value) = value.as_str() {
                    if value != importer {
                        return;
                    }
                } else {
                    return;
                }
            } else {
                return;
            }
        }

        if let Some(ref uuid) = self.uuid {
            if let Some(value) = object.get("uuid") {
                if let Some(value) = value.as_str() {
                    if value != uuid {
                        return;
                    }
                } else {
                    return;
                }
            } else {
                return;
            }
        }

        replace_json(json, self, result);
    }
}

impl ReplaceData for MetaMatch {
    fn key_value(&self) -> (&Vec<String>, &Vec<String>) {
        (&self.key, &self.value)
    }
}

#[derive(clap::Parser, Debug)]
struct KeyValue {
    /// 修改的key，如果是深层次的key，可以使用/分隔，且会转义//表示/
    #[arg(short, long, help = "修改的key，如果是深层次的key，可以使用/分隔，且会转义//表示/")]
    key: Vec<String>,
    /// 修改的value，类型根据Json的规则解析，如果是字符串，需要用引号包裹，如果是数组，需要用[]包裹
    #[arg(short, long, help = "修改的value，类型根据Json的规则解析，如果是字符串，需要用引号包裹，如果是数组，需要用[]包裹")]
    value: Vec<String>,
}

impl ReplaceData for KeyValue {
    fn key_value(&self) -> (&Vec<String>, &Vec<String>) {
        (&self.key, &self.value)
    }
}

#[derive(Parser, Debug)]
struct Cli {
    #[command(subcommand)]
    subcmd: Command,

    #[arg(help = "输入文件")]
    source: String,

    #[arg(help = "输出文件，如果不指定则直接修改源文件", short, long)]
    target: Option<String>,
}


fn main() {
    let args = Cli::parse();
    let mut json = serde_json::from_str::<serde_json::Value>(&std::fs::read_to_string(&args.source).unwrap()).unwrap();
    let mut result = Result::new();

    match &args.subcmd {
        Command::KeyValue(data) => {
            replace_json(&mut json, data, &mut result);
        }
        Command::MetaMatch(data) => {
            data.try_match(&mut json, &mut result);
        }
        Command::SubMetaMatch(data) => {
            let submeta = json.get_mut("subMetas").unwrap().as_object_mut().unwrap();
            for submeta in submeta.values_mut() {
                data.try_match(submeta, &mut result);
            }
        }
    }

    if result.changed {
        let json = serde_json::to_string_pretty(&json).unwrap();
        if let Some(target) = args.target {
            std::fs::write(target, json).unwrap();
        } else {
            std::fs::write(&args.source, json).unwrap();
        }
    }
}

fn replace_json<T: ReplaceData>(json: &mut serde_json::Value, data: &T, result: &mut Result) {
    let (key, value) = data.key_value();
    if key.len() != value.len() {
        panic!("key和value的数量不一致");
    }

    for (key, value) in key.iter().zip(value.iter()) {
        let key = parse_key(key).unwrap().1;
        let mut current = json.as_object_mut().unwrap();
        for (index, segment) in key.iter().enumerate() {
            if key.is_empty() {
                continue;
            }
            if index == key.len() - 1 {
                let new_value = serde_json::Value::from_str(value).unwrap();
                result.changed |= match current.get_mut(segment) {
                    Some(current_value) => {
                        *current_value != new_value
                    }
                    None => {
                        true
                    }
                };
                current.insert(segment.clone(), new_value);
            } else {
                result.changed |= current.get(segment).is_none();
                current = current.entry(segment.clone()).or_insert(serde_json::Value::Object(Default::default())).as_object_mut().unwrap();
            }
        }
    }
}

fn parse_segment(input: &str) -> IResult<&str, String> {
    //转义//为/
    let escaped_slash = map(tag("//"), |_| "/".to_string());
    //提取非/字符
    let normal_chars = map(is_not("/"), |s: &str| s.to_string());
    //匹配转义/或者非/字符
    let parser = many0(alt((escaped_slash, normal_chars)));
    //将匹配结果连接
    map(parser, |parts| parts.concat())(input)
}

/// 解析整个路径，按照单个 `/` 分割路径段
fn parse_key(input: &str) -> IResult<&str, Vec<String>> {
    separated_list0(char('/'), parse_segment)(input)
}
